# coding: utf-8

import json
import time

import openpyxl
from functools import wraps
from flask import Flask, send_from_directory
from flask_cors import CORS
from flask import request
from sqlalchemy.orm import sessionmaker
from lib.db.module import UserInfos, db as db_obj
from lib.tools import tail_f, md5_encrypt
from settings import LOG_DIR, REDIS_HOST, REDIS_PORT, LOGIN_TIMEOUT, \
    redis_instance
from flask_socketio import SocketIO
from celery_app.tasks import you_to_be, open_chrome

app = Flask(__name__)
# 允许所有跨域请求
CORS(app)
# 初始化SocketIO，设置允许的CORS来源, 允许所有来源进行跨域 WebSocket 请求
socketio = SocketIO(app, cors_allowed_origins="*",
                    message_queue=f'redis://{REDIS_HOST}:{REDIS_PORT}/0')


# 登录装饰器
def login_check(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        # 得到token 验证是否登陆了,且token没有过期
        local_timestamp = int(time.time())
        _token = request.headers.get("token")
        ex_time = redis_instance.get(_token)
        if not ex_time or int(local_timestamp - int(ex_time)) > LOGIN_TIMEOUT:
            return {
                'status': 400,
                'message': "请重新登录"
            }
        return func(*args, **kwargs)
    return wrapper


@app.route('/login', methods=["POST"])
def login():
    form_data = request.form.to_dict()
    username = form_data.get('username')
    password = form_data.get('password')

    Session = sessionmaker(db_obj)
    session = Session()
    resp_data = session.query(UserInfos).filter_by(username=username).all()
    if resp_data:
        pwd = resp_data[0].password
        if password != pwd:
            return {
                'status': 400,
                'message': "用户不存在或密码错误"
            }
        t = time.time()
        token = md5_encrypt(str(round(t)))
        redis_instance.set(token, t, ex=300)
        return {
            'status': 200,
            'message': "登录成功",
            'token': token
        }
    return {
        'status': 400,
        'message': "用户不存在或密码错误"
    }


@app.route('/register', methods=["POST"])
def register():
    """
        注册
    :return:
    """
    form_data = request.form.to_dict()
    username = form_data.get('username')
    password = form_data.get('password')
    crawler_id = form_data.get('crawler_id')

    Session = sessionmaker(db_obj)
    session = Session()
    resp_data = session.query(UserInfos).filter_by(username=username).all()
    if resp_data:
        return "user exist"
    else:
        if crawler_id:
            text = json.dumps(crawler_id)
            session.add(UserInfos(username=username, password=password,
                                  crawler_id=text))
        else:
            session.add(UserInfos(username=username, password=password))
        session.commit()
        return {
            'status': 200,
            'message': "注册成功"
        }


@app.route('/api/open_chrome', methods=["POST"])
def start_chrome():
    data = request.form.to_dict()
    if not data.get('port'):
        return {
            'status': 400,
            'message': "没有指定port"
        }
    print(f"port ------>: {data.get('port')}")
    open_chrome.apply_async()
    return {
        'status': 200,
        'message': "success"
    }


@app.route('/api/crawler/start', methods=["POST"])
def start_crawler():
    form_data = request.form.to_dict()
    uploaded_asset_file = request.files.get('uploadedAsset')
    if uploaded_asset_file:
        if '.xlsx' not in uploaded_asset_file.filename and '.xls' not in \
                uploaded_asset_file:
            return {
                'status': 400,
                'message': "上传的EXCEL文件格式错误, 支持格式: .xlsx 和 .xls"
            }
        # 打开上传的 Excel 文件（直接使用内存中的文件）
        workbook = openpyxl.load_workbook(uploaded_asset_file)
        sheet = workbook.active

        # 读取 Excel 文件中的每一行数据
        tag_list = []
        for row in sheet.iter_rows(values_only=True):
            for cell in row:
                if cell is None or cell == "":
                    continue
                tag_list.append(cell)
        workbook.close()
    else:
        manual_asset_ids = form_data.get('manualAsset')
        tag_list = []
        if manual_asset_ids:
            try:
                tag_list = manual_asset_ids.split(",")  # 假设手动输入的ID是用逗号分隔的
                tag_list = [tag.strip() for tag in tag_list if
                            tag.strip()]  # 去除空格
            except Exception as e:
                print(f"读取手动输入的资产ID时出错: {str(e)}")
                return {
                    'status': 400,
                    'message': f"读取手动输入的资产ID时出错: {str(e)}"
                }
    create_time = form_data.get("createTime")
    update_time = form_data.get("updateTime")
    craw_number = form_data.get("crawNumber")
    email_address_list = form_data.get("emailAddress")
    error_log = form_data.get("errorLog")
    loop_crawl = form_data.get("loopCrawl")
    log_file_name = form_data.get("log_file_name") + ".log"

    if uploaded_asset_file:
        task = you_to_be.apply_async(
            args=[tag_list, create_time, update_time, craw_number,
                  email_address_list, error_log, loop_crawl,
                  uploaded_asset_file.filename, log_file_name]
        )
    else:
        task = you_to_be.apply_async(
            args=[tag_list, create_time, update_time, craw_number,
                  email_address_list, error_log, loop_crawl,
                  None, log_file_name]
        )
    data = {
        'status': 200,
        'task.id': task.id,
        'message': "success",
        'logfile': log_file_name
    }
    return data


@app.route('/download', methods=["GET"])
def download():
    if request.method == "GET":
        data = request.args.to_dict()
        filename = data.get('filename')
        if not filename:
            return
        return send_from_directory(LOG_DIR, filename, as_attachment=True)


@socketio.on('connect')
def handle_connect():
    # 记录连接的 Socket ID
    client_id = request.args.to_dict().get('token')
    redis_instance.hset('client_ids', client_id, request.sid)


@socketio.on('init_log')
def connected_msg(file_name):
    client_id = redis_instance.hget('client_ids', str(file_name))
    data = {'message': f"client_id: {client_id} - 采集任务初始化..."}
    socketio.emit('log', data, room=client_id)

    # 日志传输
    log_file = f'{file_name}.log'
    for new_line in tail_f(log_file):
        msg = new_line.strip()
        if msg:
            socketio.emit('log', {'message': new_line}, room=client_id)
        if "Finished" in msg:
            break


if __name__ == '__main__':
    socketio.run(app, host='0.0.0.0', port=8888, debug=True)
